﻿\subsection[中断和异常的处理]{中断和异常的处理 \protect\footnote{x86 体系结构对中断和和异常的处理方式是统一的，因此在讨论对中断和异常的处理时常将二者统称为中断。}}

\subsubsection{中断处理的一般流程}

CPU 在指令周期末尾会检测是否有需要处理外部中断，内中断及异常则由 CPU 内部执行的指令触发。

所有的中断或异常事件在 x86 体系结构中均会被指派一个固定的中断向量号。
由于中断种类繁多，因此在 x86 体系结构中，由中断代理芯片（如 8259A 和 APIC）代替 CPU 接收来自 CPU 外部的中断请求（不可屏蔽中断除外）。
中断代理芯片会对接收到的若干中断信号进行筛选，决定此时最应该由 CPU 处理的中断，然后将中断信号通过 INTR 引脚送入 CPU。
若 CPU 此时处于开中断状态，则会受理该中断请求，通过 INTR 引脚向中断代理发送应答信号。
中断代理收到应答后通过总线向 CPU 传递中断向量号，CPU 根据收到的中断向量号处理中断。

中断和异常事件被 CPU 受理后，CPU 会将中断向量号作为索引从中断描述符表（Interupt Descriptor Table，IDT）的数据结构中索引到相应的中断描述符（Interupt Descriptor），从中断描述符中取出中断例程的地址，跳转到该地址处执行中断例程。
执行中断例程前，CPU 会将若干寄存器压栈保存，对某些事件类型还会再压入一个错误码。
需要保存的寄存器和寄存器保存到的栈根据中断发生前的处理器特权级而定，在讨论特权级检查时介绍。
中断例程执行结束后，一般会执行 \lstinline{iret} 指令从栈中依次弹出先前压入的若干寄存器（不含错误码，错误码需在程序中单独处理）。

图 \ref{EXTERNAL_INT_HANDLING} 展示了外中断的处理流程，中断事件源位于 CPU 外部。
内中断和异常的事件源位于 CPU 内，由 CPU 直接受理且不受 \lstinline{EFLAGS.IF} 影响，故无需经过外中断受理流程。

\begin{figure}[h!]
    \caption{外中断处理}
    \label{EXTERNAL_INT_HANDLING}
    \Centering
    \includegraphics[scale=0.6]{build/Paper/Assets/InteruptHandling.pdf}
\end{figure}

\subsubsection{中断例程与中断服务程序}

外中断对于操作系统实现至关重要。
时钟中断等外中断的触发完全独立于 CPU 中执行的程序，操作系统对计算机的控制权就是由时钟中断保证的；来自键盘等外部设备的中断保证用户与计算机之间的即时交互；磁盘设备产生的中断则是 DMA（Direct Memory Access）实现的基础。
不论是内外中断还是异常，CPU 对其的处理都依赖于 IDT 的数据结构。

CPU 最多可支持 256 个中断向量，其中有 32 个中断事件为体系结构保留。
外部中断事件种类有限，每种外中断都拥有相应的中断描述符，内中断事件（如系统调用）则种类繁多，224 个可用中断向量号不足以分配给这么多中断向量。
一般采用的做法是为这些内中断事件指定同一个中断例程（Interrupt Routine），由该中断例程根据中断向量号等信息决定采用何种方式处理该中断，即将中断派发至中断处理程序（Interrupt Handler）处理。
这样，对不同类型中断（内中断、外中断、异常）的处理入口就实现了统一，所有的中断事件均通过中断例程进行派发。
本操作系统实现暂时仅对缺页故障、时钟中断等特殊的中断和异常事件进行处理，故对于除系统调用外的中断和异常事件，仅为其创建同样的通用中断例程 \cite{os-truth}。
